<?php

/**
* Geshi Syntax Higlighting Plugin for Joomla. Uses the Geshi Highlighting Engine/Class.
* @version 1.0.1
* @copyright (c) 2006, Fiji Web Design, www.fijiwebdesign.com
* @license GNU/GPL
*/

/**
 * July 6 2008, Added support for DPSyntax
 */

defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' );

// use geshi
$_MAMBOTS->registerFunction( 'onPrepareContent', 'geshibot_onDisplayContent');


// load the geshi bot to parse content
function geshibot_onDisplayContent ($published, &$row, &$params, $page = 0 ) {
	global $mainframe;
	
	// paths
	$live_site = $mainframe->getCfg('live_site');
	$abs_path = $mainframe->getCfg('absolute_path');
	
	// check whether mambot has been unpublished
	if ( !$published ) {
		return true;
	}
	
	// params
	if (!isset($GLOBALS['GESHIBOT_PARAMS'])) {
		$GLOBALS['GESHIBOT_PARAMS'] = geshibot_getParams('geshibot', 'content'); // parameters
	}
	$botParams =& $GLOBALS['GESHIBOT_PARAMS'];
	
	// define users gid for access priviledges
	if (isset($row->modified_by) && $row->modified_by != 0) {
		$author_id = $row->modified_by;
	} else {
		$author_id = isset($row->created_by) ? $row->created_by : 0;
	}
	geshibot_setGID($author_id);
	
	//echo geshibot_object2HTML($row);
	
	// replace {joomla_dir} with Joomla Dir Path
	$row->text = str_replace('{joomla_dir}', $abs_path, $row->text);
	
	// replace code with syntax highlighted code
	if (intval($botParams->def( 'tag' )) == 0) {
	
		$GLOBALS['geshibot_regex_type'] = 0;
		$regex = "/<pre\s*(.*?)>(.*?)<\/pre>/is";
		$row->text = preg_replace_callback($regex, 'geshibot_replace_init', $row->text);
		
	} elseif (intval($botParams->def( 'tag' )) == 1) {
	
		$GLOBALS['geshibot_regex_type'] = 1;
		$regex = "/{geshibot(\s|&nbsp;)*(.*?)}(.*?){\/geshibot}/is";
		$row->text = preg_replace_callback($regex, 'geshibot_replace_init', $row->text);
		
	} elseif (intval($botParams->def( 'tag' )) == 2) {
	
		$GLOBALS['geshibot_regex_type'] = 0;
		$regex = "/<pre\s*(.*?)>(.*?)<\/pre>/is";
		$row->text = preg_replace_callback($regex, 'geshibot_replace_init', $row->text);
		
		$GLOBALS['geshibot_regex_type'] = 1;
		$regex = "/{geshibot(\s|&nbsp;)*(.*?)}(.*?){\/geshibot}/is";
		$row->text = preg_replace_callback($regex, 'geshibot_replace_init', $row->text);
		
	}
	
	if (!$botParams->def( 'use_dp' )) {
		// write geshi javascript
		if (!defined('geshibot_loaded')) {
			geshibot_load_js();
			geshibot_load_custom_css();
			define('geshibot_loaded', 1);
		}
	} else {
		// load dp javascript
		if (!defined('DPSyntax_JS')) {
			define('DPSyntax_JS', true);
			$mainframe->addCustomHeadTag('<script type="text/javascript" src="'.$live_site.'/mambots/content/geshibot/js/dpsyntax.js"></script>');
			$mainframe->addCustomHeadTag('<link type="text/css" rel="stylesheet" href="'.$live_site.'/mambots/content/geshibot/css/dpsyntax.css" />');
		}
	}

	return true;
}

// get the right match before sending it to our replace function
function geshibot_replace_init($matches) {
	$regex_type = $GLOBALS['geshibot_regex_type'];
	
	//echo geshibot_object2HTML($matches);
	
	if ($regex_type == 0) {
		return geshibot_replace($matches);
	} else {
		$_matches = array($matches[0], str_replace('&quot;', '"', $matches[2]), $matches[3]);
		return geshibot_replace($_matches);
	}
	
	
}

// callback function for preg_replace_callback
function geshibot_replace($matches) {
	
	// language
	preg_match("/(language|lang)=\"([^\"]*)\"/", $matches[1], $_matches);
	$language = isset($_matches[2]) ? $_matches[2] : ''; 
	
	// file
	preg_match("/(filepath|file)=\"([^\"]*)\"/", $matches[1], $_matches);
	$file = isset($_matches[2]) ? $_matches[2] : false; 
	
	// source, htmlDecoded
	if ($file) {
		$source = geshibot_file_get_contents($file);
	} else {
		$source = geshibot_decodeHtmlEntities(trim($matches[2]));
	}
	
	// header
	preg_match("/(header|head)=\"([^\"]*)\"/", $matches[1], $_matches);
	$header = isset($_matches[2]) ? $_matches[2] : '';
	
	// footer
	preg_match("/(footer|foot)=\"([^\"]*)\"/", $matches[1], $_matches);
	$footer = isset($_matches[2]) ? $_matches[2] : ' &nbsp; ';
	
	// retrieve the geshibot params from super globals Array
	$botParams = $GLOBALS['GESHIBOT_PARAMS'];
	
	
	if (!$botParams->def( 'use_dp' )) {
		
		// highlight code
		$h_source = geshibot_geshi ($source, $language, $header, $footer);
		
		if ($botParams->def( 'show_copy' ) == 'yes') {
			// return the highlighted code and original code in a div with unique id
			$id = (isset($GLOBALS['geshibot_load_id'])) ? $GLOBALS['geshibot_load_id']++ : 0;
			$GLOBALS['geshibot_load_id'] = $id;
			$o_source = '<div class="original_source_'.$id.'" style="display:none;">'.rawurlencode($source).'</div>';
			return '<div id="geshibot_load_'.$id.'_'.sha1($source).'">'.$h_source.$o_source.'</div>';
		} else {
			// return just the highlighted source
			return $h_source;
		}
	} else {
		return geshibot_DPHighlight($source, $language, $header, $footer);
	}
}

function geshibot_DPHighlight($source, $language = 'php', $header = '', $footer = '') {
	return '<pre name="code" class="'.($language ? $language : 'php').'">'.htmlentities($source).'</pre>';
}

function geshibot_geshi ($source, $language = 'php', $header = 'Code:', $footer = ' ') {

	// include the geshi class
	if (!require_once($GLOBALS['mosConfig_absolute_path'].'/mambots/content/geshibot/geshi.class.php')) {
		return false;
	}
	
	// First the initialisation: source code to highlight and the language to use.
	$geshi =& new GeSHi($source, $language);
	
	// retrieve the geshibot params from super globals Array
	$botParams =& $GLOBALS['GESHIBOT_PARAMS'];
	
	// Load the Geshi CSS once for each language, if set in Parameters
	if ($botParams->def( 'geshibot_css' ) == 'yes') {
		if ( !defined($language.'_css') ) {
			echo '<style type="text/css">';
			echo $geshi->get_stylesheet();
			echo '</style>';
			define($language.'_css', true);
		}
	}
    
    // Use the PRE header. This means less output source since we don't have to output &nbsp;
    // everywhere. Of course it also means you can't set the tab width.
	$geshi->set_header_type(GESHI_HEADER_PRE);
    
    // Enable CSS classes. You can use get_stylesheet() to output a stylesheet for your code. Using
    // CSS classes results in much less output source.
	$geshi->enable_classes();
    
    // Enable line numbers. We want fancy line numbers, and we want every 5th line number to be fancy
	if ($botParams->def( 'lines' ) == 'yes') {
		$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, $botParams->def( 'fancylinenum' ));
	}
    
    // Set the style for the PRE around the code. The line numbers are contained within this box (not
    // XHTML compliant btw, but if you are liberally minded about these things then you'll appreciate
    // the reduced source output).
	//$geshi->set_overall_style('color: #000066; border: 1px solid #d0d0d0; background-color: #f0f0f0;', true);
    
	// Set the style for line numbers. In order to get style for line numbers working, the <li> element
    // is being styled. This means that the code on the line will also be styled, and most of the time
    // you don't want this. So the set_code_style reverts styles for the line (by using a <div> on the line).
    // So the source output looks like this:
    //
    // <pre style="[set_overall_style styles]"><ol>
    // <li style="[set_line_style styles]"><div style="[set_code_style styles]>...</div></li>
    // ...
    // </ol></pre>
	$geshi->set_line_style('font: normal normal 95% \'Courier New\', Courier, monospace; color: #003030;', 'font-weight: bold; color: #006060;', true);
	$geshi->set_code_style('color: #000020;', 'color: #000020;');

    // Styles for hyperlinks in the code. GESHI_LINK for default styles, GESHI_HOVER for hover style etc...
    // note that classes must be enabled for this to work.
	$geshi->set_link_styles(GESHI_LINK, 'color: #000060;');
	$geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;');

    // Use the header/footer functionality. This puts a div with content within the PRE element, so it is
    // affected by the styles set by set_overall_style. So if the PRE has a border then the header/footer will
    // appear inside it.
	$geshi->set_header_content($header);
	//$geshi->set_header_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-bottom: 1px solid #d0d0d0; padding: 2px;');

    // You can use <TIME> and <VERSION> as placeholders
	$geshi->set_footer_content($footer);
	//$geshi->set_footer_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-top: 1px solid #d0d0d0; padding: 2px;');
	
	// highlight the code using geshi
	return $geshi->parse_code();
}

// get the params set for the plugin
function geshibot_getParams($botname, $folder = 'content') {
	global $database, $_MAMBOTS;
	
	if ( !isset($_MAMBOTS->_content_mambot_params[$botname]) ) {
		// load mambot params info
		$query = "SELECT params"
		. "\n FROM #__mambots"
		. "\n WHERE element = '$botname'"
		. "\n AND folder = '$folder'"
		;
		$database->setQuery( $query );
		$database->loadObject($mambot);

		// save query to class variable
		$_MAMBOTS->_content_mambot_params[$botname] = $mambot;
	}

	$mambot = $_MAMBOTS->_content_mambot_params[$botname];

 	$botParams = new mosParameters( $mambot->params );
	return $botParams;
}

// decodes html entities
function geshibot_decodeHtmlEntities($text) {
	// decode both html entity and numeric entity encoding
	$text = htmlspecialchars_decode($text);
	$text = html_entity_decode($text);

	return $text;
}

// sets the GID of the author
function geshibot_setGID($id) {
	global $database;
	$query = "SELECT gid from #__users WHERE id = $id LIMIT 1";
	$database->setQuery($query);
	$gid = $database->loadResult();
	$GLOBALS['geshibot_author_gid'] = $gid;
}

// gets the content of a file if the article author is allowed access
function geshibot_file_get_contents($file) {

	$botParams =& $GLOBALS['GESHIBOT_PARAMS'];
	
	if ($GLOBALS['geshibot_author_gid'] < intval($botParams->def( 'acl_files' ))) {
		// this user does not have enough access to get file contents
		$content = $botParams->def( 'acl_files_msg' ).' author gid:'.$GLOBALS['geshibot_author_gid'].' allowed gid:'.intval($botParams->def( 'acl_files' ));
	} else {
		$content = file_get_contents($file);
	}

	return $content;
}

// writes the JS needed for geshibot
function geshibot_load_js() {
	$botParams =& $GLOBALS['GESHIBOT_PARAMS'];
	
	?>
	<script type="text/javascript">
	<!--
	
	<?php 
	if ($botParams->def( 'joomla_css' ) == 'yes' && !defined('geshi_joomla_css')) { 
	define('geshi_joomla_css', 1);
	?>
	/**
	* Replaces the geshi default classes with mambo/joomla classes
	*/
	function replaceGeshiWithMamboClasses() { 
		if (document && document.getElementsByTagName) { 
			var pre_items = document.getElementsByTagName('pre');
			var n = pre_items.length; 
			for(var i = 0; i < n; i++) {
				// divs
				var divs = pre_items[i].getElementsByTagName('div');
				var dlen = divs.length; 
				for (var j = 0; j < dlen; j++) {
					if (divs[j].className == 'head') { // header
						var className = 'sectiontableheader';
					} else if (divs[j].className == 'foot') { // footer
						var className = 'small';
					} else if (divs[j].className == ' ') { // code lines
						var className = 'sectiontableentry1';
					} else {
						var className = '';
					}
					divs[j].className += ' '+className;
				}
				
				// anchors
				var a_items = pre_items[i].getElementsByTagName('a');
				var alen = a_items.length; 
				for (var l = 0; l < alen; l++) {
					a_items[l].target = '_blank';
					a_items[l].setAttribute('target', '_blank');
				}
			
				// list items
				var li_items = pre_items[i].getElementsByTagName('li');
				var ilen = li_items.length; 
				for (var k = 0; k < ilen; k++) {
					li_items[k].className += ' sectiontableentry1';
				}
			}
		}
	}
	// initiate our class replacements
	addLoadEvent(replaceGeshiWithMamboClasses);
	<?php } ?>
	
	<?php 
	if ($botParams->def( 'show_copy' ) == 'yes' && !defined('geshi_show_copy')) { 
	define('geshi_show_copy', 1);
	?>

	// add copy link to code highlights
	function addCodeView() { 
		if (document && document.getElementsByTagName) { 
			var divs = document.getElementsByTagName('div');
			var dlen = divs.length; 
			for (var j = 0; j < dlen; j++) {
				var pattern = new RegExp("(^|\\s)foot(\\s|$)");
				if ( pattern.test(divs[j].className) ) { // footer class
					if (divs[j]._button == 1) continue;
					<?php if (intval($botParams->def( 'show_copy_type')) == 0) { ?>
					divs[j].innerHTML += ' <input type="button" onclick="botgeshi_copy(this); return false;" value="<?php echo $botParams->def( 'show_copy_label', 'View Source' ); ?>" class="button" />';
					<?php } else { ?>
					divs[j].innerHTML += ' <a href="#" onclick="botgeshi_copy(this); return false;"><?php echo $botParams->def( 'show_copy_label', 'View Source' ); ?></a>';
					<?php } ?>
					divs[j]._button = 1;
				}
			}
		}
	}
	
	// copy the code text to new window
	function botgeshi_copy(_this) {
		if (_this && _this.parentNode) {
			var source = _this.parentNode.parentNode.nextSibling;
			var name = source.className;
			var _source = botgeshi_decode(source.innerHTML);
			_source = '<textarea style="width: 100%; height: 100%;">'+html2entities(_source)+'</textarea>';
			writeSourceWindow(_source, name, '[source view]');
		}
	}
	
	// writes content to a new window
	function writeSourceWindow(txt, name, title) {
		top.winRef = window.open('', name,
		'width=550,height=450'
		+',menubar=0'
		+',toolbar=0'
		+',status=0'
		+',scrollbars=0'
		+',resizable=1');
		top.winRef.document.writeln(
		'<html><head><title>'+title+'</title></head>'
		+'<body bgcolor=white onLoad="self.focus()">'
		+txt
		+'</body></html>'
		);
		top.winRef.document.body.style.overflow = 'hidden'; // hide scroll in FF
		top.winRef.document.close();
		top.winRef.focus();
	}
	
	// decode uriencoded text
	function botgeshi_decode(txt) {
		txt = txt.replace(/\+/g, ' ');
		if (decodeURIComponent) {
			return decodeURIComponent(txt);
		}
		if (unescape) {
			return unescape(txt);
		}
		return txt;
	}
	
	function html2entities(str){
		var re = /[<>"'&]/g
		str = str.replace(re, function(m) { return replacechar(m); });
		return str;
	}
	
	function replacechar(match){
		if (match=="<")
			return "&lt;";
		else if (match==">")
		 	return "&gt;";
		else if (match=="\"")

		 	return "&quot;";
		else if (match=="'")
		 	return "&#039;";
		else if (match=="&")
		 	return "&amp;";
	}
	
	// initiate our class replacements
	addLoadEvent(addCodeView);
	<?php } ?>
	
	/**
	* Adds an onload event to the window
	*/
	function addLoadEvent(fn) {
		if (typeof(window.onload) == 'function') {
			var _fn = window.onload;
			window.onload = function() {
				_fn();
				fn();
			}
		} else {
			window.onload = fn;
		} 
	}
	
	//-->
	</script>
	
	<?php
}

// load css
function geshibot_load_custom_css() {
	$botParams =& $GLOBALS['GESHIBOT_PARAMS'];

	// Load custom css if defined
	if ($css = $botParams->def( 'custom_css' )) {
	
		$css = str_replace('<br />', '', $css); // remove <br /> tags
	
		// create the file if it doesnt exist
		$filename = md5($css);
		$file = $GLOBALS['mosConfig_absolute_path'].'/mambots/content/geshibot/css/'.$filename.'.css';
		if (!file_exists($file)) {
			if ($fp = fopen($file, 'w')) {
				fputs($fp, $css, strlen($css));
				fclose($fp);
			}
		}
		$file = addslashes($GLOBALS['mosConfig_live_site'].'/mambots/content/geshibot/css/'.$filename.'.css');
		
		?>
		
		<script type="text/javascript">
		<!--
		addLoadEvent(function() {
			if (document && document.getElementsByTagName) {
				var head = document.getElementsByTagName('head')[0];
				var link = document.createElement('link');
				link.type = 'text/css';
				link.rel = 'stylesheet';
				link.href = '<?php echo $file; ?>';
				head.appendChild(link);
			}
		});
		//-->
		</script>
	
	<?php

	}
}

// object dump function, debugging only
define('geshi_debug', 0); // 0: off, 1: on
define('geshi_dump_type', 1); // 0: html, 1: raw txt
function geshibot_object2HTML($obj, $depth = 0) {
	if (geshi_debug == 0) return '';
	$html = '';
	if ((is_array($obj) || is_object($obj)) && count($obj) > 0) {
		foreach($obj as $i=>$x) {
			if (is_object($x) || is_array($x)) {
				$html .= '<div class="obj_parent">';
				$html .= '<span>'.$i.'</span>';
				$html .= geshibot_object2HTML($x, $depth+1);
				$html .= '</div>';
			} else {
				$html .= '<div class="obj_child" style="padding-left:10px;">'.$i.'=>'.geshi_dump2HTML($x).'</div>';
			}
		}
	} else if ((is_array($obj) || is_object($obj))) {
		$html .= '<div class="obj_child">'.var_dump($obj).'</div>'; // catch empty array
	} else {
		$html .= '<div class="obj_child">'.geshi_dump2HTML($obj).'</div>'; // cactch non-arrays/objects
	}
	return $html;
}

function geshi_dump2HTML($var) {
	if (defined('geshi_dump_type') && geshi_dump_type != 0) {
		return htmlentities($var);
	} else {
		return nl2br($var);
	}
}


if (geshi_debug == 1) {
?>	

<style type="text/css">

.obj_parent {
	border: 1px solid gray;
	margin: 1px;
}

.obj_child {
	border: 1px dotted gray;
	margin: 1px;
}

</style>

<?php } ?>